home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / quicktime / goodies / qtframestepper / qtframestepper.c < prev    next >
Encoding:
Text File  |  2000-06-23  |  10.2 KB  |  349 lines

  1. //////////
  2. //
  3. //    File:        QTFrameStepper.c
  4. //
  5. //    Contains:    Functions to step frame-by-frame through a QuickTime movie.
  6. //
  7. //    Written by:    Tim Monroe
  8. //                Parts based on DTSQTUtilities.c by Apple Developer Technical Support
  9. //                and on SimpleInMovies sample code by Guillermo A. Ortiz.
  10. //
  11. //    Copyright:    © 1997 by Apple Computer, Inc., all rights reserved.
  12. //
  13. //    Change History (most recent first):
  14. //
  15. //       <5>         11/18/98    rtm        added QTStep_GetFrameCount, for illustrative purposes only
  16. //       <4>         03/09/98    rtm        changed nextTimeMediaSample flag to nextTimeStep (to support MPEG files)
  17. //       <3>         01/05/98    rtm        revised and augmented comments
  18. //       <2>         01/02/98    rtm        some code clean-up
  19. //       <1>         12/22/97    rtm        first file
  20. //
  21. //    This file defines functions that you can use to step frame-by-frame through a QuickTime movie.
  22. //    Indeed, it illustrates *two* different methods for doing this: (1) using Movie Toolbox functions
  23. //    to advance (or retreat) to interesting times in the movie; and (2) using movie controller actions
  24. //    to step forward or backward through a movie. To my knowledge, there are no particular advantages
  25. //    to using one or the other method, except that the second method is (as you will see) quite a bit
  26. //    simpler to code.
  27. //
  28. //    METHOD ONE: Use Movie Toolbox calls to step to interesting times in the movie. An interesting time
  29. //    is a time value in a movie, track, or media that meets certain search conditions that you specify.
  30. //    We'll use a very simple search condition: locate the next (or previous) sample in the movie's media.
  31. //    Once we have an interesting time, we display the sample at that time by calling SetMovieTimeValue.
  32. //    To implement this first method, we define three functions (which all operate on an open movie):
  33. //
  34. //    -> QTStep_GoToNextVideoSample: display the sample that follows the current sample in a movie
  35. //    -> QTStep_GoToPrevVideoSample: display the sample that precedes the current sample in a movie
  36. //    -> QTStep_GoToFirstVideoSample: display the first video sample in a movie
  37. //
  38. //    Internally, these functions depend on three static functions defined at the beginning of this file.
  39. //    The code here is extremely straightforward. The only "gotcha" concerns finding the first
  40. //    interesting time in a movie. See the description of QTStep_GetStartTimeOfFirstVideoSample for
  41. //    details.
  42. //
  43. //    METHOD TWO: Use movie controller actions to step through frames in the movie. This method uses
  44. //    the MCDoAction function with the mcActionStep and mcActionGoToTime actions. Using this method,
  45. //    the code is considerably simpler. To implement this second method, we define three functions
  46. //    (which all operate on a movie controller that is associated with an open movie):
  47. //
  48. //    -> QTStep_MCGoToNextVideoSample: display the sample that follows the current sample in a movie
  49. //    -> QTStep_MCGoToPrevVideoSample: display the sample that precedes the current sample in a movie
  50. //    -> QTStep_MCGoToFirstVideoSample: display the first video sample in a movie
  51. //
  52. //    Historical note: Method One is based loosely on sample-stepping code in the DTSQTUtilities package
  53. //    developed by Apple Macintosh Developer Technical Support. Method Two is based on a few functions
  54. //    in the SimpleInMovies sample code written by Guillermo A. Ortiz.
  55. //
  56. //////////
  57.  
  58. #include "QTFrameStepper.h"
  59.  
  60.  
  61. //////////
  62. //
  63. // METHOD ONE: Use Movie Toolbox calls to step to interesting times in the movie.
  64. //
  65. //////////
  66.  
  67. //////////
  68. //
  69. // QTStep_GetStartTimeOfFirstVideoSample
  70. // Return, through the theTime parameter, the starting time of the first video sample of the
  71. // specified QuickTime movie.
  72. //
  73. // The "trick" here is to set the nextTimeEdgeOK flag, to indicate that you want to get the
  74. // starting time of the beginning of the movie.
  75. //
  76. // If this function encounters an error, it returns a (bogus) starting time of -1. Note that
  77. // GetMovieNextInterestingTime also returns -1 as a starting time if the search criteria
  78. // specified in the myFlags parameter are not matched by any interesting time in the movie. 
  79. //
  80. //////////
  81.  
  82. static OSErr QTStep_GetStartTimeOfFirstVideoSample (Movie theMovie, TimeValue *theTime)
  83. {
  84.     short            myFlags;
  85.     OSType            myTypes[1];
  86.     
  87.     *theTime = kBogusStartingTime;                            // a bogus starting time
  88.     if (theMovie == NULL)
  89.         return(invalidMovie);
  90.     
  91.     myFlags = nextTimeMediaSample + nextTimeEdgeOK;            // we want the first sample in the movie
  92.     myTypes[0] = VisualMediaCharacteristic;                    // we want video samples
  93.  
  94.     GetMovieNextInterestingTime(theMovie, myFlags, 1, myTypes, (TimeValue)0, fixed1, theTime, NULL);
  95.     return(GetMoviesError());
  96. }
  97.  
  98.  
  99. //////////
  100. //
  101. // QTStep_DrawVideoSampleAtTime
  102. // Draw the video sample of a QuickTime movie at the specified time.
  103. //
  104. //////////
  105.  
  106. static OSErr QTStep_DrawVideoSampleAtTime (Movie theMovie, TimeValue theTime)
  107. {
  108.     short            myFlags;
  109.     OSErr            myErr = noErr;
  110.     
  111.     if (theMovie == NULL)
  112.         return(invalidMovie);
  113.     
  114.     // make sure that the specified time lies within the movie's temporal bounds
  115.     if ((theTime < 0) || (theTime > GetMovieDuration(theMovie)))
  116.         return(paramErr);
  117.     
  118.     SetMovieTimeValue(theMovie, theTime);
  119.     myErr = GetMoviesError();
  120.     if (myErr != noErr)
  121.         goto bail;
  122.         
  123.     // the following calls to UpdateMovie and MoviesTask are not necessary
  124.     // if you are handling movie controller events in your main event loop
  125.     // (by passing the event to MCIsPlayerEvent); they don't hurt, however.
  126.     
  127.     // redraw the movie immediately by calling UpdateMovie and MoviesTask
  128.     UpdateMovie(theMovie);
  129.     myErr = GetMoviesError();
  130.     if (myErr != noErr)
  131.         goto bail;
  132.         
  133.     MoviesTask(theMovie, 0L);
  134.     myErr = GetMoviesError();
  135.  
  136. bail:
  137.     return(myErr);
  138. }
  139.  
  140.  
  141. //////////
  142. //
  143. // QTStep_DrawVideoSampleNextOrPrev
  144. // Draw the next or previous video sample of a QuickTime movie.
  145. // If theRate is 1, the next video sample is drawn; if theRate is -1, the previous sample is drawn.
  146. //
  147. //////////
  148.  
  149. static OSErr QTStep_DrawVideoSampleNextOrPrev (Movie theMovie, Fixed theRate)
  150. {
  151.     TimeValue        myCurrTime;
  152.     TimeValue        myNextTime;
  153.     short            myFlags;
  154.     OSType            myTypes[1];
  155.     OSErr            myErr = noErr;
  156.     
  157.     if (theMovie == NULL)
  158.         return(invalidMovie);
  159.     
  160.     myFlags = nextTimeStep;                                    // we want the next frame in the movie's media
  161.     myTypes[0] = VisualMediaCharacteristic;                    // we want video samples
  162.     myCurrTime = GetMovieTime(theMovie, NULL);
  163.  
  164.     GetMovieNextInterestingTime(theMovie, myFlags, 1, myTypes, myCurrTime, theRate, &myNextTime, NULL);
  165.     myErr = GetMoviesError();
  166.     if (myErr != noErr)
  167.         return(myErr);
  168.         
  169.     myErr = QTStep_DrawVideoSampleAtTime(theMovie, myNextTime);
  170.     
  171.     return(myErr);
  172. }
  173.  
  174.  
  175. //////////
  176. //
  177. // QTStep_GoToFirstVideoSample
  178. // Draw the first video sample of a QuickTime movie.
  179. //
  180. //////////
  181.  
  182. OSErr QTStep_GoToFirstVideoSample (Movie theMovie)
  183. {
  184.     TimeValue        myTime;
  185.     OSErr            myErr = noErr;
  186.     
  187.     if (theMovie == NULL)
  188.         return(invalidMovie);
  189.         
  190.     myErr = QTStep_GetStartTimeOfFirstVideoSample(theMovie, &myTime);
  191.     if (myErr != noErr)
  192.         return(myErr);
  193.         
  194.     myErr = QTStep_DrawVideoSampleAtTime(theMovie, myTime);
  195.     return(myErr);
  196. }
  197.  
  198.  
  199. //////////
  200. //
  201. // QTStep_GoToNextVideoSample
  202. // Draw the next video sample of a QuickTime movie.
  203. //
  204. //////////
  205.  
  206. OSErr QTStep_GoToNextVideoSample (Movie theMovie)
  207. {
  208.     return(QTStep_DrawVideoSampleNextOrPrev(theMovie, fixed1));
  209. }
  210.  
  211.  
  212. //////////
  213. //
  214. // QTStep_GoToPrevVideoSample
  215. // Draw the previous video sample of a QuickTime movie.
  216. //
  217. //////////
  218.  
  219. OSErr QTStep_GoToPrevVideoSample (Movie theMovie)
  220. {
  221.     return(QTStep_DrawVideoSampleNextOrPrev(theMovie, FixMul(Long2Fix(-1), fixed1)));
  222. }
  223.  
  224.  
  225. //////////
  226. //
  227. // METHOD TWO: Use movie controller actions to step thru frames in the movie.
  228. //
  229. //////////
  230.  
  231. //////////
  232. //
  233. // QTStep_MCGoToFirstVideoSample
  234. // Draw the first video sample of the QuickTime movie associated with the specified movie controller.
  235. //
  236. //////////
  237.  
  238. OSErr QTStep_MCGoToFirstVideoSample (MovieController theMC)
  239. {
  240.     TimeRecord        myTimeRecord;
  241.     Movie             myMovie = NULL;
  242.     OSErr             myErr = noErr;
  243.     
  244.     if (theMC == NULL)
  245.         return(paramErr);
  246.         
  247.     myMovie = MCGetMovie(theMC);
  248.     if (myMovie == NULL)
  249.         return(paramErr);
  250.     
  251.     myTimeRecord.value.hi = 0;
  252.     myTimeRecord.value.lo = 0;
  253.     myTimeRecord.base = 0;
  254.     myTimeRecord.scale = GetMovieTimeScale(myMovie);
  255.     myErr = GetMoviesError();
  256.     if (myErr != noErr)
  257.         return(myErr);
  258.     
  259.     return(MCDoAction(theMC, mcActionGoToTime, &myTimeRecord));
  260. }
  261.  
  262.  
  263. //////////
  264. //
  265. // QTStep_MCGoToNextVideoSample
  266. // Draw the next video sample of the QuickTime movie associated with the specified movie controller.
  267. //
  268. //////////
  269.  
  270. OSErr QTStep_MCGoToNextVideoSample (MovieController theMC)
  271. {
  272.     short            myStep = 1;                // advance the movie one frame
  273.     
  274.     if (theMC == NULL)
  275.         return(paramErr);
  276.         
  277.     return(MCDoAction(theMC, mcActionStep, (Ptr)myStep));
  278. }
  279.  
  280.  
  281. //////////
  282. //
  283. // QTStep_MCGoToPrevVideoSample
  284. // Draw the previous video sample of the QuickTime movie associated with the specified movie controller.
  285. //
  286. //////////
  287.  
  288. OSErr QTStep_MCGoToPrevVideoSample (MovieController theMC)
  289. {
  290.     short            myStep = -1;            // back the movie up one frame
  291.     
  292.     if (theMC == NULL)
  293.         return(paramErr);
  294.         
  295.     return(MCDoAction(theMC, mcActionStep, (Ptr)myStep));
  296. }
  297.  
  298.  
  299. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  300. //
  301. // Frame utilities.
  302. //
  303. // These functions illustrate some other useful things you might want to do with movie frames;
  304. // they are not used elsewhere in this file.
  305. //
  306. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  307.  
  308. //////////
  309. //
  310. // QTStep_GetFrameCount
  311. // Get the number of frames in the specified movie track. We return the value -1 if
  312. // an error occurs and we cannot determine the number of frames in the track.
  313. //
  314. // Based (loosely) on frame-counting code in ConvertToMovie Jr.c.
  315. // 
  316. // We count the frames in the track by stepping through all of its interesting times
  317. // (the places where the track displays a new sample).
  318. //
  319. //////////
  320.  
  321. long QTStep_GetFrameCount (Track theTrack)
  322. {    
  323.     long        myCount = -1;
  324.     short        myFlags;
  325.     TimeValue    myTime = 0;
  326.     
  327.     if (theTrack == NULL)
  328.         goto bail;
  329.         
  330.     // we want to begin with the first frame (sample) in the track
  331.     myFlags = nextTimeMediaSample + nextTimeEdgeOK;
  332.  
  333.     while (myTime >= 0) {
  334.         myCount++;
  335.         
  336.         // look for the next frame in the track; when there are no more frames,
  337.         // myTime is set to -1, so we'll exit the while loop
  338.         GetTrackNextInterestingTime(theTrack, myFlags, myTime, fixed1, &myTime, NULL);
  339.         
  340.         // after the first interesting time, don't include the time we're currently at
  341.         myFlags = nextTimeStep;
  342.     }
  343.  
  344. bail:
  345.     return(myCount);
  346. }
  347.  
  348.  
  349.